/**
 * @(#)BaseDAOFactory.java Nov 8, 2006
 *
 * Copyright (c) 2005-2010 Psiqueware S.A. de C.V. All Rights Reserved.
 * This software is the proprietary information Psiqueware S.A. de C.V.
 * Use is subject to license terms.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * <br/>Usuario : AngelElectrico
 * <br/>Creaci&oacute;n: Nov 8, 2006 - 5:09:59 PM
 * <br/>
 * <br/>
 * <br/><t5>Cambios :</t5>
 * <br/><b>Fecha | Autor  |  Cambios</b>
 * <ul>
 * <li>Nov 8, 2006 | <a href = "mailto:AngelElectrico@PsiqueWare.com">Angel El&eacute;ctrico</a> | Creaci&oacute;n del Objeto.</li>
 *
 * </ul>
 */

package com.psiqueware.alheli.dao;

import com.psiqueware.alheli.dao.fake.EnlaceTransaccionalFake;
import com.psiqueware.alheli.dao.hibernate.EnlaceHibernateClasico;
import com.psiqueware.alheli.logging.Logger;
import com.psiqueware.alheli.logging.LoggerFactory;
import com.psiqueware.alheli.reflection.pooling.PoolObjeto;
import com.psiqueware.alheli.reflection.pooling.PoolObjetoListaInstancias;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

/**
 * Clase B&aacute;sica para una f&aacute;brica de DAO.
 * <br/>Paquete Original: com.psiqueware.basico.dao
 * <br/>Nombre  Original: BaseDAOFactory
 * <br/>Oprima para ver : <a href="BaseDAOFactory.java.html"><i>C&oacute;digo Fuente</i> </a>
 *
 * @author <a href="mailto:AngelElectrico@PsiqueWare.com">Angel El&eacute;ctrico</a>
 * @version Revision: 1.1 Nov 8, 2006 5:09:59 PM
 */
public abstract class BaseDAOFactory
{

    /**
     * Logger de clase para BaseDAOFactory
     */
    private static final Logger s_log = LoggerFactory.getLogger(BaseDAOFactory.class);

    /**
     * Mapa con los mapas que contienen los pool de operaciones CRUD para cada TipoDAO.
     */
    private static final Map<Class, Map<TipoDAO, PoolObjeto<BaseDAO>>> s_mapaPoolDAOs = new HashMap<Class, Map<TipoDAO, PoolObjeto<BaseDAO>>>();

    /**
     * Tipo de dao utilizado en este esquema de persistencia.
     *
     * @noinspection StaticVariableOfConcreteClass
     */
    protected static TipoDAO m_tipo = TipoDAO.FAKE;


    /**
     * Constructor de BaseDAOFactory.
     */
    public BaseDAOFactory()
    {
    }

    /**
     * Devuelve el Tipo de dao utilizado en este esquema de persistencia.
     *
     * @return valor de 'm_tipo'.
     *
     * @noinspection MethodReturnOfConcreteClass
     * @see TipoDAO
     */
    public static TipoDAO getTipo()
    {
        return m_tipo;
    }

    /**
     * Establece el Tipo de dao utilizado en este esquema de persistencia.
     *
     * @param tipo nuevo valor para 'm_tipo'.
     *
     * @see TipoDAO
     */
    public static void setTipo(TipoDAO tipo)
    {
        m_tipo = tipo;
    }

    /**
     * Devuelve un nuevo enlace transaccional para el tipo DAO elegido.
     *
     * @return EnlaceTransaccional dependiendo de TipoDAO.
     */
    public static EnlaceTransaccional nuevoEnlace()
    {
        EnlaceTransaccional enlace = null;
        if (m_tipo.equals(TipoDAO.FAKE))
        {
            enlace = new EnlaceTransaccionalFake();
        }
        else if (m_tipo.equals(TipoDAO.DUMMY))
        {
            //enlace = new EnlaceHibernateAnnotations ();
        }
        else if (m_tipo.equals(TipoDAO.HIBERNATE_CLASICO))
        {
            enlace = new EnlaceHibernateClasico();
        }
        else if (m_tipo.equals(TipoDAO.HIBERNATE_ANNOTATIONS))
        {
            //enlace = new EnlaceHibernateAnnotations ();
        }
        else if (m_tipo.equals(TipoDAO.ORACLE))
        {
            //enlace = new EnlaceHibernateAnnotations ();
        }

        if (enlace == null)
        {
            String error = "<msg>No se encontr&oacute; un EnlaceTransaccional correcto para el tipo:" + m_tipo + "</msg>";
            s_log.error(error);
            throw new IllegalArgumentException(error);
        }
        return enlace;
    }

    /**
     * Devuelve un nuevo enlace transaccional para el tipo DAO elegido.
     *
     * @return EnlaceTransaccional dependiendo de TipoDAO.
     */
    public static EnlaceTransaccional nuevoEnlace(final TipoDAO tipo)
    {
        EnlaceTransaccional enlace = null;
        if (tipo.equals(TipoDAO.FAKE))
        {
            enlace = new EnlaceTransaccionalFake();
        }
        else if (tipo.equals(TipoDAO.DUMMY))
        {
            //enlace = new EnlaceHibernateAnnotations ();
        }
        else if (tipo.equals(TipoDAO.HIBERNATE_CLASICO))
        {
            enlace = new EnlaceHibernateClasico();
        }
        else if (tipo.equals(TipoDAO.HIBERNATE_ANNOTATIONS))
        {
            //enlace = new EnlaceHibernateAnnotations ();
        }
        else if (tipo.equals(TipoDAO.ORACLE))
        {
            //enlace = new EnlaceHibernateAnnotations ();
        }

        if (enlace == null)
        {
            String error = "<msg>No se encontr&oacute; un EnlaceTransaccional correcto para el tipo:" + tipo + "</msg>";
            s_log.error(error);
            throw new IllegalArgumentException(error);
        }
        return enlace;
    }

    /**
     * Devuelve una instancia del dao para la clase de modelo de dominio.
     * <p/>
     * Esta operaci&oacute;n asume que los DAOs existen en el paquete 'dao' y que
     * este se encuentra al mismo nivel que el paquete de la clase del
     * modelo de dominio la cual se debe encontrar en un paquete llamado
     * 'modelo'. Adicionalmente las clase de DAO deber&aacute;n nombrarse con
     * el nombre de la clase de dominio mas el posfijo especificado en el
     * TipoDAO.
     *
     * @param claseModeloDominio clase de modelo de dominio cuyo DAO se esta buscando.
     * @param enlace             para obtener su tipo.
     *
     * @return LoginDAO para operaciones CRUD.
     *
     * @noinspection unchecked, ChainedMethodCall, StaticMethodOnlyUsedInOneClass
     */
    public static synchronized BaseDAO findDAO(final Class claseModeloDominio, final EnlaceTransaccional enlace)
    {
        Map<TipoDAO, PoolObjeto<BaseDAO>> mapa = s_mapaPoolDAOs.get(claseModeloDominio);
        if (null == mapa)
        {
            mapa = new EnumMap<TipoDAO, PoolObjeto<BaseDAO>>(TipoDAO.class);
            final String paquete = claseModeloDominio.getPackage().getName().replace(".modelo", "") + ".dao";
            final String name = claseModeloDominio.getSimpleName();
            for (final TipoDAO tipoDAO : TipoDAO.values())
            {
                String nombreClase = paquete + "." + tipoDAO.getPosfijo().toLowerCase() + "." + name + "DAO" + tipoDAO.getPosfijo();
                Class<?> claseDAO = null;
                try
                {
                    claseDAO = Class.forName(nombreClase);
                }
                catch (final ClassNotFoundException ignored)
                {
                    if (s_log.isDebugEnabled()) s_log.debug("<msg>" + nombreClase + " no fue encontrada.</msg>");
                }

                if (null == claseDAO && (tipoDAO == TipoDAO.HIBERNATE_CLASICO || tipoDAO == TipoDAO.HIBERNATE_ANNOTATIONS))
                {
                    nombreClase = paquete + ".hbm." + name + "DAO" + tipoDAO.getPosfijo();
                    try
                    {
                        claseDAO = Class.forName(nombreClase);
                    }
                    catch (final ClassNotFoundException ignored)
                    {
                        if (s_log.isDebugEnabled()) s_log.debug("<msg>" + nombreClase + " no fue encontrada.</msg>");
                    }
                }

                if (null != claseDAO)
                {
                    mapa.put(tipoDAO, new PoolObjetoListaInstancias<BaseDAO>(claseDAO));
                }
            }
            s_mapaPoolDAOs.put(claseModeloDominio, mapa);
        }

        final PoolObjeto<BaseDAO> pool = mapa.get(enlace.getTipo());
        if (null == pool) throw new DAONoEncontrado(enlace.getTipo(), claseModeloDominio);
        final BaseDAO baseDAO = pool.referencia();
        if (null == baseDAO) throw new DAONoEncontrado(enlace.getTipo(), claseModeloDominio);
        return baseDAO;
    }

}

